﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ChMetody_2
{
    class Gaus
    {
        /// <summary>
        /// Решение СЛАУ методом Гаусса с выбором главного элемента(по столбцу)
        /// </summary>
        /// <param name="A">Матрица коефициентов СЛАУ</param>
        /// <param name="b">Массив коефициентов строки</param>
        /// <param name="x">Массив решений</param>
        /// <param name="e">Точность</param>
        /// <param name="n">Порядок матрици</param>
        /// <returns>возвращает true - если СЛАУ имеет решение</returns>
        static public bool Gauss(ref double[,] A, ref double[] b, ref double[] x, double e, int n)
        {
           for (int i=0; i<n-1; i++)
            {
                int im=MaxStb(A,n,i);
                PerestStr(ref A, ref b, n, i, im);
                if (Math.Abs(A[i, i]) > e)
                {
                    for (int j = i + 1; j < n; j++)
                    {
                        double v = A[j, i] / A[i, i];
                        A[j, i] = 0;
                        for (int k = i + 1; k < n; k++)
                            A[j, k] = A[j, k] - v * A[i, k];
                        b[j] = b[j] - v * b[i];
                    }
                }
                else
                    return false;
            }
            if (Math.Abs(A[n-1, n-1]) < e)
                return false;
            else
            {
                x[n-1] = b[n-1] / A[n-1, n-1];
                for (int i = n - 2; i >= 0; i--)
                {
                    double s = 0;
                    for (int j = i + 1; j < n; j++)
                        s = s + A[i, j] * x[j];
                    x[i] = (b[i] - s) / A[i, i];
                }
                return true;
            }
        }
        /// <summary>
        /// Нахождение найбольшего коефициента по строкам(по модулю)
        /// </summary>
        /// <param name="A">Матрица коефициентов СЛАУ</param>
        /// <param name="n">Порядок матрици</param>
        /// <param name="ii">Номер строки с которого начинается поиск (вниз)</param>
        /// <returns>возвращает номер найбольшего элемента по строкам</returns>
        static public int MaxStb(double[,] A, int n, int ii)
        {
            double Cmax = Math.Abs(A[ii, ii]);
            int Kmax = ii;
            for (int i=ii+1; i<n; i++)
                if (Cmax < Math.Abs(A[i, ii]))
                {
                    Kmax = i;
                    Cmax = Math.Abs(A[i, ii]);
                }
            return Kmax;
        }
        /// <summary>
        /// Перестановка строк
        /// </summary>
        /// <param name="A">Матрица коефициентов СЛАУ</param>
        /// <param name="b">Массив коефициентов строки</param>
        /// <param name="n">Порядок матрици</param>
        /// <param name="ip">Строка меняющаяся со строкой ik</param>
        /// <param name="ik">Строка меняющаяся со строкой ip</param>
        static public void PerestStr(ref double[,] A, ref double[] b, int n, int ip, int ik)
        {
            if (ip == ik) return;

            for (int i = ip; i < n; i++)
            {
                double p = A[ip, i];
                A[ip, i] = A[ik, i];
                A[ik, i] = p;
            }
            double p1 = b[ip];
            b[ip] = b[ik];
            b[ik] = p1;
        }

        static public void Check(ref double[] R, out double abs_R, double[,] A,double[] b, double[] x, int n)
        {
            abs_R = 0;            
            for (int i = 0; i < n; i++)
            {
                R[i] = 0;
                abs_R = abs_R + abs_R * abs_R;
                for (int j = 0; j < n; j++)
                    R[i] = R[i] + A[i, j] * x[j];
                R[i] = R[i] - b[i];
            }
        }


        /// <summary>
        /// Нахождение обратной матрицы
        /// </summary>
        /// <param name="A">Входная матрица</param>
        /// <param name="X">Обратная матрица</param>
        /// <param name="eps">Точность</param>
        /// <param name="n">Размер матрици</param>
        /// <returns>Если возможно найти обратную матрицу возвращает Труе если нет - фалсе</returns>
        static public bool ObratM(ref double[,] A, ref double[,] X, double eps, int n)
        {
            double[,] B = new double[n, n];
            for (int i = 0; i < n; ++i)
                for (int j = 0; j < n; ++j)
                    if (i == j)
                    {
                        B[i, j] = 1;
                    }
                    else
                    {
                        B[i, j] = 0;
                    }

            double[] V = new double[n];
            double[] Q = new double[n];

            for (int m = 0; m < n; ++m)
            {
                for (int i = 0; i < n; ++i)
                {
                    V[i] = B[i, m];
                }

                double[,] O = new double[n,n];
                for (int w = 0; w < n; ++w)
                    for (int h = 0; h < n; ++h)
                        O[w, h] = A[w, h];


                bool f = Gauss(ref O, ref V, ref Q, 0.0000000001, n);
                if (f == false) return false;

                for (int i = 0; i < n; ++i)
                {
                    X[i,m] = Q[i];
                }
            }
            return true;
        }

    }
}
